
#include "process.h"
#include "tread.h"
#include "disk.h"
#include "simulator.h"
#include "observer.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>


static int fill_buf(char *buf, int bufMax);
static int parse_trace(char *buf, struct process **p);
void tread_die(char *);

enum parseMode {PM_NAME, PM_START_TIME, PM_TIME, PM_IO};

int nextEventAt;
char inBuf[TREAD_LINE_MAX+1];
struct process *nextProc;

/* By Jacob Eichers and Cliff Buchanan
	Reads in the data from the trace file and outputs the data as processes*/


/* initalize trace reader. Must be performed before any other
	tread calls are made.*/
void tread_init()
{
	nextProc = NULL;

	fill_buf(inBuf, TREAD_LINE_MAX+1);
	nextEventAt = parse_trace(inBuf, &nextProc);
}

/* return time of next process arrival. Defined only when tread_done()==0*/
int tread_time_to_event()
{
	if(nextEventAt>sim_get_clock())
		return nextEventAt-sim_get_clock();
	else
		{return 0;}
}

/* returns 0 iff tread has no more traces*/
int tread_done()
{
	return !nextProc;
}

/* returns the next trace, provied tread_done()==0*/
struct process *tread_next()
{
	struct process *p = nextProc;

	fill_buf(inBuf, TREAD_LINE_MAX+1);
	nextEventAt = parse_trace(inBuf, &nextProc);
	

	obs_tread_next(p);

	return p;
}

/* helper method. Read input to a buffer*/
static int fill_buf(char *buf, int bufMax)
{
	int c, bufLen;

	/* fill buffer*/
	bufLen = 0;
	do
	{
		c = getchar();

		if(bufLen>bufMax-1)
		{ 
			perror("tread.c: Line exceeds maximum length");
			exit(-1);
		}
		if (c!=EOF && c!='\n' && c!='\0')
		{
			buf[bufLen] = c;
			bufLen++;
		}
	}
	while (c!=EOF && c!='\n');
	buf[bufLen]='\0';

	return bufLen;
}

/* Helper method. Parse a null terminate trace string into a process.
	Return startTime for the trace.*/
static int parse_trace(char *trace, struct process **p)
{
	const int msInS = 1000;
	int i;
	int len = 0;
	char *name, *t;
	int startTime, io;
	int lifeTime;
	enum parseMode pm;
	
	if(!trace[0])
	{
		*p = NULL;
		return -1;
	}
	
	i = 0;
	for( pm=PM_NAME; pm<=PM_IO; pm++ )
	{
		/* skip whitespace*/
		while (isblank(trace[i]) && trace[i])
			{i++;}

		/* read arg until next whitespace*/
		len = 0;
		while(!isblank(trace[i+len]) && trace[i+len])
			{len++;}

		/* convert substring into a process field*/
		switch(pm)
		{
			case PM_NAME:
				/* copy name into a new string*/
				name = calloc(sizeof(char), len+1);
				strncpy(name, &(trace[i]), len);
				name[len] = '\0';
			break;
			case PM_START_TIME:
				startTime = 10*(int)strtol(&(trace[i]), &t, 10);
			break;
			case PM_TIME:
				lifeTime = (int)((msInS*strtof(&(trace[i]), &t)) + 0.5);
				
			break;
			case PM_IO:
				io = (int)strtol(&(trace[i]), &t, 10);
			break;
			default: /*shouldn't happen*/
				tread_die("parse_trace");
			break;
		}
		/* if len is 0 or if we strtoed and didn't read the end, close*/
		if( pm!=PM_NAME && &(trace[i+len])!=t )
		{
			fprintf(stderr, "tread.c: invalid number format");
			tread_die("parse_trace");
		}

		/* advance i to the end of argument*/
		i += len;
	}
	
	*p = proc_create(name, startTime, lifeTime, io);
	if(!(*p))
		{tread_die("parse_trace");}
	
	free(name);
	return startTime;
}

void tread_die(char *caller)
{
	fprintf(stderr, "%s: critical error! Exiting...\n", caller);
	exit(-1);
}
